import configparser
import pickle
import shutil

import paramiko
import os
import hashlib
import time

from PyQt5.QtCore import pyqtBoundSignal
from ftpclient import FtpClient, FtpLoginInfo
from settings import BASE_DIR, ENCODING, IS_REAL_TIME_SYNC



# ---- 配置文件 ----
from settings import BASE_DIR, conf
from utils.publicFun import save_obj, load_obj

config = {}
config['hide'] = [
    'node_modules'
]
config['local_dir'] = "./dist"
config['host'] = ""
config['user'] = "local_dir"
config['pass'] = ""
config['port'] = 22
config['remote_dir'] = "/var/www/"

# ---- 配置结束 ----

_cache_hash = {}
_is_init = True

_create_file_list = []
_modify_file_list = []
_delete_file_list = []
_have_upload = []

class Sftp:
    # transport

    # webpath = ""
    # host = ""
    # username = ""
    # password = ""
    # port = 22
    def __init__(self, host, port=22, username="", password=""):
        self.host = host
        self.username = username
        self.password = password
        self.port = port
        self.ftp = FtpClient.ftp()

    def connect(self):
        self.ftp.reconnect()



    def mkdir(self, dirname):
        stdin, stdout, stderr = self.transport.exec_command('mkdir ' + dirname)
        # 获取命令结果
        result = stdout.read()

        def command(self, command):
            stdin, stdout, stderr = self.transport.exec_command(command)
            # 获取命令结果

        result = stdout.read()
        return result


def close(self):
    self.transport.close()


print("连接SFTP...")
# 实例化SFTP
sftp = Sftp(config['host'], config['port'], config['user'], config['pass'])


# 生成MD5
def md5(str):
    m2 = hashlib.md5()
    m2.update(str.encode('utf8'))
    return m2.hexdigest()


# 获取文件的创建时间
def get_FileCreateTime(filePath):
    # filePath = unicode(filePath,'utf8')
    t = os.path.getctime(filePath)
    return t


# 获取文件的修改时间
def get_FileModifyTime(filePath):
    # filePath = unicode(filePath,'utf8')
    t = os.path.getmtime(filePath)
    return t


# 判断文件是否发生变动
def has_change(filepath):
    return False


def is_hidden(filename):
    if (filename.startswith('.')):
        return True
    if (filename in config['hide']):
        return True
    return False


def clear_list():
    # print("清空列表")#清内存
    del _modify_file_list[:]
    del _create_file_list[:]
    del _delete_file_list[:]
    del _have_upload[:]

def list_dir(local_dir):
    # global _cache_hash
    if os.path.exists(local_dir):
        for filename in os.listdir(local_dir):
            pathname = os.path.join(local_dir, filename)
            filehash = md5(pathname)  # 文件的hash值
            try:
                create_time = get_FileCreateTime(pathname)  # 文件创建时间
            except:
                print('文件不存在或已删除')
                continue
            modify_time = get_FileModifyTime(pathname)  # 文件修改时间
            isfile = os.path.isfile(pathname)

            # 判断是否隐藏的文件
            if (False == is_hidden(filename)):
                if (False == isfile):
                    # print('modify_time_dir=======', filename)
                    # _cache_hash[filehash] = {"create_time": create_time, "modify_time": modify_time, "path": pathname}
                    if (filehash in _cache_hash):  # 有缓存
                        # 文件发生了改变
                        if (_cache_hash[filehash]["modify_time"] != modify_time):
                            if (False == isfile):
                                _cache_hash[filehash]["modify_time"] = modify_time
                                if _cache_hash[filehash]["create_time"] != create_time:
                                    # print(_cache_hash)
                                    _modify_file_list.append(pathname)  # 文件夹也要同步上传

                        pass
                    else:  # 没有缓存
                        _cache_hash[filehash] = {"create_time": create_time, "modify_time": modify_time, "path": pathname}
                        _create_file_list.append(pathname)

                    list_dir(pathname)

                else:
                    # print(pathname)
                    # 是否已经缓存
                    if (filehash in _cache_hash):  # 有缓存
                        # 文件发生了改变
                        if (_cache_hash[filehash]["modify_time"] != modify_time):
                            if (False == isfile):
                                list_dir(pathname)
                                _cache_hash[filehash]["modify_time"] = modify_time
                            else:
                                _modify_file_list.append(pathname)# 文件同步
                            _cache_hash[filehash]["modify_time"] = modify_time
                    else:  # 没有缓存 初始化
                        # print('debug')
                        _cache_hash[filehash] = {"create_time": create_time, "modify_time": modify_time, "path": pathname}
                        _create_file_list.append(pathname)
    else:
        print(local_dir, '不存在,取消同步')
        raise #抛出异常让程序终止
    return _cache_hash





# 开始连接SFTP


def upload(file_list, local_dir, remote_dir, jobName='client'):
    print('real_time_local_dir', local_dir)
    try:
        sftp.ftp.reconnect()
        for filename in file_list:
            # if filename in _have_upload:
            #     continue
            file_list_item = filename
            isfile = os.path.isfile(filename)
            config = configparser.ConfigParser()
            config.read(os.path.join(BASE_DIR, conf), encoding=ENCODING)
            is_real_time = config.get(jobName, 'is_real_time')

            if is_real_time == '0':
                print('close sync', IS_REAL_TIME_SYNC)
                return 0
            # print('save_obj ========= ', filename)
            # time.sleep(0.2)
            if isfile:
                filename = filename.replace("\\", "/").replace(local_dir, "", 1).replace('/', "", 1)  # 匹配第一次避免/1122/test/12.txt这种全被替换
                # print((time.strftime('%H:%M:%S', time.localtime(time.time())) +
                #        ':' + filename.replace(local_dir, "", 1)).ljust(50, ' '), end='')
                try:
                    # print('filename_remote_dir', filename, remote_dir)
                    sftp.ftp.upload_file(local_dir, filename, sftp.ftp, remote_dir)
                except:
                    pass
            else:
                filename = filename.replace("\\", "/")
                remote = remote_dir + filename.replace(local_dir, "", 1)
                # print('remote_dir', remote_dir)
                # print('filename', filename)
                # print('remote', remote)
                if remote_dir == '/':#防止remote=/\\新建文件夹,新建第一层文件夹时候
                    remote = remote_dir + filename.replace(local_dir, "", 1).replace("\\", "", 1)#replace("\\", "", 1)这里的1防止多层新建的时候都替换了
                # print('远程目录_', remote)
                # print(filename)
                # sftp.ftp.upload_dir(filename, remote, True)#filename此时是本地完整路径目录
                sftp.ftp.mkd_dir(filename, remote)
            # print('\b' * 20, end='')#下一行打印不换行
            # print('[ok]'.rjust(20, ' '))#20个空格后打印ok
            # _have_upload.append(file_list_item)
        print('传输完成')
    except Exception as e:
        print("连接问题:中断或超时", e)
def backupAndClear(local_dir):
    for file_name in os.listdir(local_dir):
        filenames = local_dir + r'/{}'.format(file_name)
        if os.path.isdir(filenames):
            shutil.rmtree(filenames)
        else:
            os.remove(filenames)
    refresh_cachehash()
def real_time_sync(jobName, remote_dir, signal: pyqtBoundSignal):
    # 初始化
    # print("初始化...")
    # list_dir(local_dir)
    # global _cache_hash
    # global sftp, config
    # sftp = Sftp(config['host'], config['port'], config['user'], config['pass'])
    sftp.ftp = FtpClient.ftp()
    print("开始监控文件...", IS_REAL_TIME_SYNC)
    counts = 0
    refresh_cachehash()#清空缓存
    while (True):

        global _cache_hash
        # if os.path.exists(os.path.join(BASE_DIR, "_cache_hash.pkl")):#获取手动同步过的文件(夹)
        #     _cache_hash = load_obj("_cache_hash")
        config = configparser.ConfigParser()
        config.read(os.path.join(BASE_DIR, conf), encoding=ENCODING)
        is_real_time = config.get(jobName, 'is_real_time')
        local_dir = config.get(jobName, 'local_dir')
        remote_dir = config.get(jobName, 'remote_dir')
        if is_real_time == '0':
            print('break', IS_REAL_TIME_SYNC)
            break
            return
        # else:
        #     counts = counts+1
        #     if counts != 1:
        #     # 清空列表 初始化的数据不记录首次不清理,取消同步之后再打开继续传
        #         clear_list()
        #     pass
        clear_list()
        # 遍历文件目录
        ldir = list_dir(local_dir)
        _cache_statistics = load_obj("_cache_statistics")
        _cache_statistics["total"] = len(ldir.keys())
        _cache_statistics["succeeded"] = 0
        # if _cache_statistics["succeeded"] == _cache_statistics["total"]:
        #     clear_list()
        save_obj(_cache_statistics, "_cache_statistics")
        # signal.emit('共有文件及文件夹' + len(ldir.keys()).__str__())
        # 获取对应修改的文件
        # 删除的文件
        for filename in _delete_file_list:
            print(filename + ' 删除')
        # 新建的文件
        if (len(_create_file_list) > 0):
            # 开始上传
            print("create...", _create_file_list)
            try:
                isbreak = upload(_create_file_list, local_dir, remote_dir, jobName)

                shutil.copytree(local_dir, local_dir.split('/')[0] + r'/ftp__/{}'.format('__备份') + local_dir[local_dir.index('/'):] + time.strftime('%Y%m%d%H%M%S',
                                                                                          time.localtime(time.time())))
                if isbreak != 0:
                    backupAndClear(local_dir)
                else:
                    break
            except Exception as e:
                print('upload_failed', e)
        # 修改的文件
        if (len(_modify_file_list) > 0):
            # 开始上传
            print("modify...")
            try:
                isbreak = upload(_modify_file_list, local_dir, remote_dir, jobName)
                shutil.copytree(local_dir, local_dir.split('/')[0] + r'/ftp__/{}'.format('__备份') + time.strftime('%Y%m%d%H%M%S',
                      time.localtime(time.time())))
                if isbreak != 0:
                    backupAndClear(local_dir)
                else:
                    break
            except Exception as e:
                print('upload_failed', e)
        # print('list_dir _create_file_list ', _create_file_list)


        # 清空记录 准备下一轮
        # clear_list()
        save_obj(_cache_hash, "_cache_hash")
        # print('save_obj =========_cache_hash ', _cache_hash)
        time.sleep(1)
def refresh_cachehash():
    global _cache_hash
    _cache_hash = {}
if __name__=='__main__':
    local_dir = '/src/ui'
    while (True):
        list_dir(local_dir)
        time.sleep(3)